use actix_web::{error, post, web, HttpResponse, Responder};
use diesel::prelude::Insertable;
use diesel::MysqlConnection;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::AppWebData;
use crate::aiobs_mock::schema::*;
use crate::diesel::RunQueryDsl;

/// User details.
#[derive(Debug, Clone, Serialize, Deserialize, Queryable)]
#[diesel(table_name = users)]
pub struct User {
    pub id: i32,
    pub name: String,
}

/// New user details.
#[derive(Debug, Clone, Serialize, Deserialize, Insertable)]
#[diesel(table_name = users)]
pub struct NewUser {
    pub name: String,
}

type DbError = Box<dyn std::error::Error + Send + Sync>;

#[post("/callResultCallback")]
async fn call_result_callback(
    app_web_data: web::Data<AppWebData>,
    form: web::Json<NewUser>,
) -> actix_web::Result<impl Responder> {
    // use web::block to offload blocking Diesel queries without blocking server thread
    let user = web::block(move || {
        // note that obtaining a connection from the pool is also potentially blocking
        let mut conn = app_web_data.db_conn_pool.get()?;
        insert_new_user(&mut conn, &form.name)
    })
    .await?
    // map diesel query errors to a 500 error response
    .map_err(error::ErrorInternalServerError)?;

    // user was added successfully; return 201 response with new user info
    Ok(HttpResponse::Created().json(user))
}

/// Run query using Diesel to insert a new database row and return the result.
pub fn insert_new_user(
    conn: &mut MysqlConnection,
    nm: &str, // prevent collision with `name` column imported inside the function
) -> Result<NewUser, DbError> {
    // It is common when using Diesel with Actix Web to import schema-related
    // modules inside a function's scope (rather than the normal module's scope)
    // to prevent import collisions and namespace pollution.
    use crate::aiobs_mock::schema::users::dsl::*;

    let new_user = NewUser {
        name: nm.to_owned(),
    };

    let result = diesel::insert_into(users).values(&new_user).execute(conn)?;

    Ok(new_user)
}
